/**
 * @file filter
 * @author liaoxuezhi@baidu.com
 */
/* eslint fecs-indent: [0, "space", 2, 2] */

import * as React from 'react';
import * as update from 'react-addons-update';
import {
  PureComponent,
  PropTypes
} from 'react';
import {Renderer, fetch} from '../renderer';
import * as cx from 'classnames';
import {alert} from '../../widgets/alert/alert';
import * as assign from 'object-assign';
import Html from '../../widgets/html';
import * as schema from './tasks-schema.json';


@Renderer({
  name: 'tasks',
  desc: '任务表',
  test: (path, config) => /(?:^|\/)tasks$/.test(path)
    || /(?:^|\/)form-item$/.test(path) && 'tasks' === config.type,
  schema,
  validateSchema: true
})
export default class Tasks extends PureComponent {
  static propTypes = {
    className: PropTypes.string,
    items: PropTypes.arrayOf(PropTypes.shape({
      label: PropTypes.string.isRequired,
      key: PropTypes.any.isRequired,
      remark: PropTypes.string
    })),
    checkApi: PropTypes.string,
    submitApi: PropTypes.string,
    reSubmitApi: PropTypes.string
  };

  static defaultProps = {
    className: 'b-a bg-white table-responsive',
    tableClassName: 'table table-striped m-b-none',
    taskNameLabel: '任务名称',
    operationLabel: '操作',
    statusLabel: '状态',
    remarkLabel: '备注说明',
    btnText: '上线',
    retryBtnText: '重试',
    btnClassName: 'btn-sm btn-default',
    retryBtnClassName: 'btn-sm btn-danger',
    statusLabelMap: ['label-warning', 'label-info', 'label-info', 'label-danger', 'label-success', 'label-danger'],
    statusTextMap: ['未开始', '就绪', '进行中', '出错', '已完成', '出错'],
    initialStatusCode: 0,
    readyStatusCode: 1,
    loadingStatusCode: 2,
    errorStatusCode: 3,
    finishStatusCode: 4,
    canRetryStatusCode: 5,
    interval: 3000
  };

  timer = null;

  constructor(props) {
    super(props);
    this.state = {
      items: props.items ? props.items.concat() : []
    };

    this.handleLoaded = this.handleLoaded.bind(this);
    this.tick = this.tick.bind(this);
  }

  componentWillReceiveProps(nextProps) {
    const props = this.props;

    if (props.items !== nextProps.items) {
      this.setState({
        items: nextProps.items ? nextProps.items.concat() : []
      });
    }
  }

  componentDidMount() {
    this.tick(!!this.props.checkApi);
  }

  componentWillUnmount() {
    clearTimeout(this.timer);
  }

  tick(force = false) {
    const {
      loadingStatusCode,
      data,
      interval,
      checkApi
    } = this.props;
    const items = this.state.items;

    // 如果每个 task 都完成了, 则不需要取查看状态.
    if (!force && !items.some(item => item.status === loadingStatusCode)) {
      return;
    }

    if (interval && !checkApi) {
      return alert('checkApi 没有设置, 不能及时获取任务状态');
    }

    fetch(checkApi, data)
      .then(this.handleLoaded)
      .catch(e => this.setState({error: e}))
  }

  handleLoaded(ret) {
    if (!Array.isArray(ret.data)) {
      return alert('返回格式不正确, 期望 response.data 为数组, 包含每个 task 的状态信息');
    }

    this.setState({
      items: ret.data
    });

    const interval = this.props.interval;
    clearTimeout(this.timer);
    this.timer = setTimeout(this.tick, interval);
  }

  submitTask(item, index, retry = false) {
    const {
      submitApi,
      reSubmitApi,
      loadingStatusCode,
      errorStatusCode,
      data
    } = this.props;

    if (!retry && !submitApi) {
      return alert('submitApi 没有配置');
    } else if (retry && !reSubmitApi) {
      return alert('reSubmitApi 没有配置');
    }

    this.setState(update(this.state, {
      items: {
        $splice: [
          [index, 1, assign({}, item, {
            status: loadingStatusCode
          })]
        ]
      }
    }));

    fetch(retry ? reSubmitApi : submitApi, assign({}, data, item))
      .then(ret => {
        if (ret && ret.data) {
          if (Array.isArray(ret.data)) {
            this.handleLoaded(ret);
          } else {
            const items = this.state.items.map(item => item.key === ret.key ? assign({}, item, ret) : item);
            this.handleLoaded({data: items});
          }
          return;
        }

        clearTimeout(this.timer);
        this.timer = setTimeout(this.tick, 4);
      })
      .catch(e => this.setState(update(this.state, {
        items: {
          $splice: [
            [index, 1, assign({}, item, {
              status: errorStatusCode,
              remark: e.message || e
            })]
          ]
        }
      })))
  }

  render() {
    const {
      className,
      tableClassName,
      taskNameLabel,
      operationLabel,
      statusLabel,
      remarkLabel,
      btnText,
      retryBtnText,
      btnClassName,
      retryBtnClassName,
      statusLabelMap,
      statusTextMap,
      readyStatusCode,
      loadingStatusCode,
      canRetryStatusCode
    } = this.props;
    const items = this.state.items;
    const error = this.state.error;

    return (
      <div className={className}>
        <table className={tableClassName}>
          <thead>
            <tr>
              <th>{taskNameLabel}</th>
              <th>{operationLabel}</th>
              <th>{statusLabel}</th>
              <th>{remarkLabel}</th>
            </tr>
          </thead>
          <tbody>
          {error ? (
            <tr>
              <td colspan="4"><div className="text-danger">{error}</div></td>
            </tr>
          ) : items.map((item, key) => (
            <tr key={key}>
              <td>{item.label}</td>
              <td>
                {item.status == loadingStatusCode ? (
                  <i className="fa fa-spinner fa-spin fa-2x fa-fw" />
                ) : item.status == canRetryStatusCode ? (
                  <a
                    onClick={() => this.submitTask(item, key, true)}
                    className={cx('btn', retryBtnClassName || btnClassName)}
                  >
                    {retryBtnText || btnText}
                  </a>
                ) : (
                  <a
                    onClick={() => this.submitTask(item, key)}
                    className={cx('btn', btnClassName, {disabled: item.status !== readyStatusCode})}
                  >
                    {btnText}
                  </a>
                )}
              </td>
              <td><span className={cx('label', statusLabelMap[item.status || 0])}>{statusTextMap[item.status || 0]}</span></td>
              <td><Html html={item.remark} /></td>
            </tr>
          ))}
          </tbody>
        </table>
      </div>
    );
  }
}